

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.swing.JPanel;

/**
* RandomCircles.java, created on 09/13/2011
* @author BaoTam_Vo
* @version 1.1
*/

@SuppressWarnings("serial")
public class GraphDrawerPanel extends JPanel {
	public static Dimension SIZE = new Dimension(700,700);
	private AdjacencyList adjList;
	private GraphOutline graphOutline;
	private Structure currStructure;
	
	private class Edge extends Line2D.Double {
		public boolean equals(Object obj) {
			if(!(obj instanceof Line2D.Double)) {
				return false;
			}
			else {
				Line2D.Double line = (Line2D.Double) obj;
				if(((this.getP1().equals(line.getP1()))&&(this.getP2().equals(line.getP2())))||
						((this.getP2().equals(line.getP1()))&&(this.getP1().equals(line.getP2())))) {
					return true;
				}
				else {
					return false;
				}
			}
			
		}
	}
	
	private class GraphOutline {
		private Point center = new Point(SIZE.width/2,SIZE.height/2);
		private int	radius = (SIZE.height-50)/2;
		private int vertexSize = 20;
		private Map<Vertex, Point> vertexMap = new HashMap<Vertex, Point>();
		private Map<Line2D.Double, Integer> edgeMap = new HashMap<Line2D.Double, Integer>();
		
		
		private Point rotate(Point point, double angle) {
			int newX = (int)(point.x * Math.cos(angle) - point.y * Math.sin(angle));
			int newY = (int)(point.x * Math.sin(angle) + point.y * Math.cos(angle));

			return new Point(newX, newY);
		}
		
		
		public GraphOutline(AdjacencyList adjList) {
			Set<Vertex> vertexSet = adjList.getVertexSet();
			Iterator<Vertex> vertexSetIterator = adjList.getVertexSet().iterator();
			
			double availAngle = 2*Math.PI;
			double rotateAngle = availAngle/vertexSet.size();
			Point currVertexPoint = new Point(0, radius);
			
			while(vertexSetIterator.hasNext()) {
				vertexMap.put(vertexSetIterator.next(), translateAxes(currVertexPoint));
				currVertexPoint = rotate(currVertexPoint, rotateAngle);
			}
			
			vertexSetIterator = adjList.getVertexSet().iterator();
			while(vertexSetIterator.hasNext()) {
				Vertex currVertex = vertexSetIterator.next();
				Iterator<Pair> adjSetIterator = adjList.getAdjacencies(currVertex).iterator();
				while(adjSetIterator.hasNext()) {
					Pair currPair = adjSetIterator.next();
					edgeMap.put(new Line2D.Double(vertexMap.get(currVertex),vertexMap.get(currPair.getVertex())), currPair.getWeight());
				}
			}
			
		}
		
		public Point translateAxes(Point point) {
			return new Point(SIZE.width/2 + point.x, SIZE.height/2 - point.y);
		}
		
		public void draw(Graphics g) {
			g.setColor(Color.gray);
			g.drawOval(center.x - radius, center.y - radius, radius*2, radius*2);
		}
		
		public void drawVertices(Graphics g) {
			Iterator<Vertex> iterator = vertexMap.keySet().iterator();
			Random random = new Random(System.nanoTime());
			while(iterator.hasNext()) {
				Vertex currVertex = iterator.next();
				Point vertexPoint = vertexMap.get(currVertex);
				g.setColor(new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
				g.fillOval(vertexPoint.x - vertexSize/2, vertexPoint.y - vertexSize/2, vertexSize, vertexSize);
				g.setColor(Color.white);
				g.drawString(Integer.toString(currVertex.getId()), vertexPoint.x - vertexSize/4, vertexPoint.y + vertexSize/4);
			}
		}
		
		public void drawEdges(Graphics g) {
			  Iterator<Line2D.Double> iterator = edgeMap.keySet().iterator();
			  Random random = new Random(System.nanoTime());
			  while(iterator.hasNext()) {
				  Line2D.Double line = iterator.next();
				  int x1 = (int)line.x1;
				  int x2 = (int)line.x2;
				  int y1 = (int)line.y1;
				  int y2 = (int)line.y2;
				  g.setColor(Color.lightGray);
				  g.drawLine(x1, y1, x2, y2);
				  //g.setColor(Color.white);
				  g.drawString(edgeMap.get(line).toString(),(x1+x2)/2, (y1+y2)/2);
			  }
		}
		
		public Vertex withinVertex(Point point) {
			Iterator<Vertex> iterator = vertexMap.keySet().iterator();
			Random random = new Random(System.nanoTime());
			Vertex result = null;
			while(iterator.hasNext()) {
				Vertex currVertex = iterator.next();
				Point vertexPoint = vertexMap.get(currVertex);
				if(Math.hypot(vertexPoint.x - point.x, vertexPoint.y - point.y) < vertexSize/2) {
					return currVertex;
				}
			}
			return result;
		}
		
		public void updateVertexPoint(Vertex vertex, Point newPoint) {
			vertexMap.put(vertex, newPoint);
			edgeMap = new HashMap<Line2D.Double, Integer>();
			Iterator<Vertex> vertexSetIterator = vertexMap.keySet().iterator();
			while(vertexSetIterator.hasNext()) {
				Vertex currVertex = vertexSetIterator.next();
				Iterator<Pair> adjSetIterator = adjList.getAdjacencies(currVertex).iterator();
				while(adjSetIterator.hasNext()) {
					Pair currPair = adjSetIterator.next();
					edgeMap.put(new Line2D.Double(vertexMap.get(currVertex),vertexMap.get(currPair.getVertex())), currPair.getWeight());
				}
			}
		}
	}
	
	public GraphDrawerPanel(AdjacencyList adjList) {
		this.adjList = adjList;
		this.graphOutline = new GraphOutline(adjList);
		LocalMouseListener listener = new LocalMouseListener();
		this.addMouseListener(listener);
		this.addMouseMotionListener(listener);
		setPreferredSize(SIZE);
	}
	
	public void setAdjList(AdjacencyList adjList) {
		this.adjList = adjList;
		this.graphOutline = new GraphOutline(adjList);
		repaint();
	}
	
	
	
   public void paint(Graphics g) {
	   	if(adjList == null) {
	   		return;
	   	}
	   	
	   	Graphics2D g2d = (Graphics2D) g;
	   	g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
	   	g2d.setColor(Color.white);
	   	g2d.fillRect(0,0,getPreferredSize().width,getPreferredSize().height);
	   	graphOutline.draw(g2d);
	   	graphOutline.drawEdges(g2d);
	   	graphOutline.drawVertices(g2d);
	   	/*
		  final int rad = 50;
		  int c1x, c1y, c2x, c2y, c3x, c3y;
		  
		  // Find window size
		  int wid = getWidth();
		  int hei = getHeight();
		  
		  // Set background to white
		  g.setColor(Color.white);
		  g.fillRect(0,0,wid,hei);
		  
		  // Create random number generator object
		  Random gen = new Random();
		  
		  // Draw first circle
		  c1x = gen.nextInt(wid);
		  c1y = gen.nextInt(hei);
		  g.setColor(Color.red);
		  g.fillOval(c1x-rad,c1y-rad,rad*2,rad*2);
		  System.out.println(c1x + " " + c1y);
		  
		  // Draw second circle
		  c2x = gen.nextInt(wid);
		  c2y = gen.nextInt(hei);
		  g.setColor(Color.green);
		  g.fillOval(c2x-rad,c2y-rad,rad*2,rad*2);
		  System.out.println(c2x + " " + c2y);
		  
		  // Draw third circle
		  c3x = gen.nextInt(wid);
		  c3y = gen.nextInt(hei);
		  g.setColor(Color.blue);
		  g.fillOval(c3x-rad,c3y-rad,rad*2,rad*2);
		  System.out.println(c3x + " " + c3y);
		  System.out.println();
		  
		  // Draw line and show distance from first to second circle
		  drawLineVertexToVertex(g, c1x, c1y, c2x, c2y);
		  	  
		  // Draw line and show distance from first to third circle
		  drawLineVertexToVertex(g, c1x, c1y, c3x, c3y);
		  
		  // Draw line and show distance from third to second circle
		  drawLineVertexToVertex(g, c3x, c3y, c2x, c2y);		  
		  */
	}
   
   public Structure getCurrStructure() {
	return currStructure;
}

public void setCurrStructure(Structure currStructure) {
	this.currStructure = currStructure;
}

private class LocalMouseListener implements MouseListener, MouseMotionListener {
	   Vertex currVertex = null;
	   
		@Override
		public void mouseClicked(MouseEvent event) {
			
		}
	
		@Override
		public void mouseEntered(MouseEvent event) {
			
		}
	
		@Override
		public void mouseExited(MouseEvent event) {
			
		}
	
		@Override
		public void mousePressed(MouseEvent event) {
			currVertex = graphOutline.withinVertex(event.getPoint());
		}
	
		@Override
		public void mouseReleased(MouseEvent event) {
			
		}

		@Override
		public void mouseDragged(MouseEvent e) {
			if(currVertex == null) {
				return;
			}
			graphOutline.updateVertexPoint(currVertex, e.getPoint());
			
			repaint();
		}

		@Override
		public void mouseMoved(MouseEvent e) {
			
		}
	   
   }
   
   

}

        
        
